package customer

import (
	"context"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize"
	"github.com/google/wire"
	"go.manyji.com/lib/util/convert"
	"reflect"
	"sieve_admin_server/app/dao"
	"sieve_admin_server/app/dao/user_records"
	"sieve_admin_server/app/schema"
	"sieve_admin_server/app/schema/customer"
	serviceSys "sieve_admin_server/app/service/sys"
	serviceUtil "sieve_admin_server/app/service/util"
	pkgTime "sieve_admin_server/pkg/time"
	"sieve_admin_server/pkg/util"
	pkgUtil "sieve_admin_server/pkg/util"
	"strings"
	"time"
)

var (
	UserRecordsSet = wire.NewSet(wire.Struct(new(UserRecordsSrv), "*"))
)

type UserRecordsSrv struct {
	UserSrv         *UserSrv
	RoleSrv         *serviceSys.RoleSrv
	UserRecordsRepo *dao.UserRecordsRepo
}

func (a *UserRecordsSrv) Query(ctx context.Context, uid uint64, reqArgs map[string]interface{}, p schema.PaginationParam) interface{} {

	var (
		err                  error
		creatorNameLike      string
		creatorRoleNameLike  string
		creatorRoleAliasLike string
		rolePerssion         string
	)

	if _, ok := reqArgs["creator_name"]; ok {
		creatorNameLike = "AND user.account = '" + reqArgs["creator_name"].(string) + "'"
		delete(reqArgs, "creator_name")
	}

	if _, ok := reqArgs["creator_role_name"]; ok {
		creatorRoleNameLike = "AND role.name = '" + reqArgs["creator_role_name"].(string) + "'"
		delete(reqArgs, "creator_role_name")
	}

	if _, ok := reqArgs["creator_role_alias"]; ok {
		creatorRoleAliasLike = "AND role.alias = '" + reqArgs["creator_role_alias"].(string) + "'"
		delete(reqArgs, "creator_role_alias")
	}

	if _, ok := reqArgs["creator_name_like"]; ok {
		creatorNameLike = "AND user.account LIKE " + "'%" + reqArgs["creator_name_like"].(string) + "%'"
		delete(reqArgs, "creator_name_like")
	}

	if _, ok := reqArgs["creator_role_name_like"]; ok {
		creatorRoleNameLike = "AND role.name LIKE " + "'%" + reqArgs["creator_role_name_like"].(string) + "%'"
		delete(reqArgs, "creator_role_name_like")
	}

	if _, ok := reqArgs["creator_role_alias_like"]; ok {
		creatorRoleAliasLike = "AND role.alias LIKE " + "'%" + reqArgs["creator_role_alias_like"].(string) + "%'"
		delete(reqArgs, "creator_role_alias_like")
	}

	roleId, err := a.UserSrv.GetRoleID(ctx, uid)
	if err != nil {
		return err
	}

	if a.RoleSrv.IsSalespersonRole(roleId) {
		rolePerssion = fmt.Sprintf(" AND (user.id = %v OR user.leader_id = %v OR user.top_leader_id = %v)", uid, uid, uid)

		ctx = context.WithValue(ctx, "group_by", "id")
	}

	var (
		columnType = customer.QueryUserOpRecordResp{}
		joinString = []string{
			fmt.Sprintf(`INNER JOIN user ON user.id = user_op_record.creator AND user.deleted = 0 %v %v`, creatorNameLike, rolePerssion),
			fmt.Sprintf(`INNER JOIN role ON role.id = user.role_id AND role.deleted = 0 %v %v`, creatorRoleNameLike, creatorRoleAliasLike),
		}

		repKeys = append(util.Struct2Slice(user_records.UserOpRecord{}, "json", "user_op_record"), []string{
			"user.account as creator_name",
			"role.id as creator_role_id",
			"role.name as creator_role_name",
			"role.alias as creator_role_alias",
		}...)
	)

	ctx = context.WithValue(
		context.WithValue(ctx, "join", strings.Join(joinString, " ")),
		"column_types", columnType,
	)

	req, err := a.UserRecordsRepo.Query(ctx, reqArgs, p, repKeys)
	if err != nil {
		return err
	}

	if req != nil {

		var (
			t = customer.QueryUserOpRecordResp{}
		)
		for _, v := range req.(map[string]interface{})["list"].([]interface{}) {

			respRecord := v.(*customer.QueryUserOpRecordResp)
			t.PointsConsumeTotal += respRecord.PointsConsumeTotal
			t.PointsConsumeAccount += respRecord.PointsConsumeAccount
			t.PointsConsumeGift += respRecord.PointsConsumeGift
			t.RechargePoints += respRecord.RechargePoints
			t.BonusPoints += respRecord.BonusPoints
			t.RefundPoints += respRecord.RefundPoints
			t.ResidualPoints += respRecord.ResidualPoints
			t.AgentPoints += respRecord.AgentPoints
			t.Profit += respRecord.Profit
		}

		req.(map[string]interface{})["all"] = map[string]interface{}{
			"points_consume_total":   t.PointsConsumeTotal,
			"points_consume_account": t.PointsConsumeAccount,
			"points_consume_gift":    t.PointsConsumeGift,
			"recharge_points":        t.RechargePoints,
			"bonus_points":           t.BonusPoints,
			"refund_points":          t.RefundPoints,
			"residual_points":        t.ResidualPoints,
			"agent_points":           t.AgentPoints,
			"profit":                 t.Profit,
		}
	}
	return req
}

func (a *UserRecordsSrv) Create(ctx context.Context, userId uint64, item map[string]interface{}) interface{} {

	var (
		now = time.Now().Unix()
		def = map[string]interface{}{
			"creator":    userId,
			"create_at":  now,
			"updated_at": now,
		}
	)

	for k, v := range def {
		item[k] = v
	}

	return a.UserRecordsRepo.Create(ctx, item)
}

func (a *UserRecordsSrv) Export(ctx context.Context, reqArgs map[string]interface{}, fCallBack func(fileName string)) interface{} {

	var (
		p = schema.PaginationParam{
			OnlyData: true,
		}
		queryArgs = map[string]interface{}{
			"id": func() string {
				ids := reqArgs["ids"].(string)
				return fmt.Sprintf("(%v)", strings.Join(strings.Split(ids, "|"), ","))
			}(),
		}
	)

	ctx = context.WithValue(ctx,
		"condition", map[string]string{"id": "IN"},
	)

	req := a.Query(ctx, adminAccount.ID, queryArgs, p)
	if req != nil {
		if err, ok := req.(error); ok {
			return err
		}
	}

	selectedItems := []string{
		"id",
		"business_type_name",
		"op_type_name",
		"points_consume_total",
		"points_consume_gift",
		"recharge_points",
		"bonus_points",
		"refund_points",
		"residual_points",
		"creator_name",
		"creator_role_name",
		"create_at",
	}
	req = a.export(ctx, req, 1, strings.Join(selectedItems, "|"))
	if req != nil {
		if _, ok := req.(error); ok {
			return req
		}
	}

	fCallBack(req.(string))

	return req
}

func (a *UserRecordsSrv) export(ctx context.Context, req interface{}, funcTy int, selectedItems string) interface{} {

	var (
		xlsx       = excelize.NewFile()
		categories = map[string]string{
			"A1": "序号",
			"B1": "业务类型",
			"C1": "操作类型",
			"D1": "消费积分",
			"E1": "赠送消费积分",
			"F1": "充值积分",
			"G1": "充值赠送积分",
			"H1": "退款积分",
			"I1": "剩余积分",
			"J1": "创建人",
			"K1": "创建人角色",
			"L1": "创建时间",
		}
		selectedItemMap = make(map[string]int)
		colWidths       = map[string]float64{"A": 8, "B": 22, "C": 18, "D": 10, "E": 10, "F": 10, "G": 10, "H": 10, "I": 10, "J": 12, "K": 12, "L": 20}
	)

	for key, value := range categories {
		xlsx.SetCellValue("Sheet1", key, value)
		_style, err := xlsx.NewStyle(`{"fill":{"type":"pattern","color":["#EEECE1"],"pattern":1},"alignment":{"horizontal":"center","vertical":"center"},"font":{"bold":true,"family":"宋体","size":10}}`)
		if err == nil {
			xlsx.SetCellStyle("Sheet1", key, key, _style)
		}
	}

	xlsx.SetRowHeight("Sheet1", 1, 20)

	for k, v := range colWidths {
		xlsx.SetColWidth("Sheet1", k, k, v)
	}

	for i, v := range strings.Split(fmt.Sprintf("%v", selectedItems), "|") {
		selectedItemMap[v] = i
	}

	p := map[string]struct{}{
		"points_consume_total": {},
		"points_consume_gift":  {},
		"recharge_points":      {},
		"bonus_points":         {},
		"refund_points":        {},
		"residual_points":      {},
	}
	v := req.(map[string]interface{})
	for index, vv := range v["list"].([]interface{}) {

		var (
			columIndex  = 'A'
			columOffset = 1
		)

		for kkk, vvv := range util.Struct2Map(vv, "json") {

			if len(selectedItemMap) > 0 {
				if i, ok := selectedItemMap[kkk]; !ok {
					continue
				} else {
					columOffset = i
				}
			}

			cell := fmt.Sprintf("%c", int(columIndex)+columOffset) + convert.ToString(index+2)

			if reflect.TypeOf(vvv).Kind() == reflect.Ptr {
				vOf := reflect.ValueOf(vvv)
				if vOf.IsNil() {
					vvv = ""
				} else {
					vvv = vOf.Elem()
				}
			}

			if kkk == "create_at" {
				vvv = pkgTime.Format(vvv.(int64))
			}

			if _, ok := p[kkk]; ok {
				vvv = pkgUtil.Decimal(float64(vvv.(uint64))/10000.0, 5)
			}

			xlsx.SetCellValue("Sheet1", cell, vvv)
		}

		xlsx.SetRowHeight("Sheet1", index+2, 18)
	}

	xlsx.SetSheetName("Sheet1", "第一页")

	fileName, filePath := a.getExportPath(convert.ToString(time.Now().UnixMilli())+".csv", 1)
	err := xlsx.SaveAs(fileName)
	if err != nil {
		return nil
	}

	req = filePath

	return req
}

func (a *UserRecordsSrv) getExportPath(fileName string, bizTy int) (string, string) {

	var (
		bizTyMap = map[int]string{
			1: "user_op_record",
		}
	)

	relativePath := fmt.Sprintf("%v/export/%v", bizTyMap[bizTy], fileName)

	return serviceUtil.GetFilePath(
		fmt.Sprintf("%v/%v",
			serviceUtil.FileStorage(),
			relativePath),
	), relativePath
}
